package com.tenbent.product.base.convert;

import com.tenbent.product.base.utils.DateUtil;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.format.annotation.DateTimeFormat;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

import javax.persistence.Column;
import javax.persistence.Id;

/**
 * 反射转换器
 *
 * 1. map 转 bo
 *
 * 2. bo 转 map 同时处理日期类型
 *
 * 3. 验证属性字符串集合是否跟bo属性一直( 判断属性本身，判断注解属性)
 */
public class ObjectTypeConvert {

	private static final Logger logger = LoggerFactory.getLogger(ObjectTypeConvert.class);

	/**
	 * 根据Map里的key映射到对应的BO类
	 * 
	 * @param <T>BO的类型
	 * @param paramMap
	 *            field - value 的map
	 * @param clazz
	 *            BO类的class
	 * @return 对应field值置完值的BO
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 * @throws NoSuchFieldException
	 */
	public static <T> T formMapToBO(Map paramMap, Class<T> clazz)
			throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException,
			IllegalArgumentException, InvocationTargetException, NoSuchFieldException {

		Map map = new HashMap();
		if (paramMap == null)
			return null;
		T obj = clazz.newInstance();
		for (String key : new ArrayList<String>(paramMap.keySet())) {
			Field filed;
			/**
			 * clazz.getDeclaredField只能获得在这个类型的声明中定义的指定名称的属性， 父类中的属性无法获取。
			 */
			// try {
			// filed = clazz.getDeclaredField(key);
			// } catch (NoSuchFieldException e) {
			// filed = null;
			// }
			try {
				filed = getField(clazz, key);
			} catch (Exception exp) {
				filed = null;
			}
			if (null != filed) {
				Class[] param = { filed.getType() };
				String propertyName = filed.getName();
				Method m;
				try {
					m = clazz.getMethod("set" + propertyName.substring(0, 1).toUpperCase()
							+ propertyName.substring(1, propertyName.length()), param);
				} catch (NoSuchMethodException e) {
					m = clazz.getMethod(
							"set" + propertyName.substring(0, 1) + propertyName.substring(1, propertyName.length()),
							param);
				}
				Object paramObj = paramMap.get(key);
				Object covertObj;
				if (paramObj != null) {
					if (filed.getType() == String.class) {
						covertObj = String.valueOf(paramObj);
					} else if (filed.getType() == Integer.class || filed.getType() == int.class) {
						covertObj = Integer.parseInt(paramObj.toString());
					} else {
						// do some else covert
						covertObj = paramObj;
					}
				} else {
					covertObj = paramObj;
				}
				m.invoke(obj, covertObj);

			} else {

				map.put(key, paramMap.get(key));
			}

		}
		if (!map.isEmpty()) {
			Method m = null;
			try {
				m = clazz.getMethod("setSpecialProps", Map.class);
			} catch (NoSuchMethodException e) {
				logger.warn("No Such Method SpecialProps");
				return obj;
			}
			m.invoke(obj, map);
		}
		return obj;
	}

	/**
	 * 获取对应类中的属性，当子类中不存在该属性时，采用递归从父类中获取。
	 * 
	 * @param clazz
	 * @param key
	 * @return
	 */
	public static Field getField(Class clazz, String key) {
		Field field;
		try {
			Field[] fields = clazz.getDeclaredFields();
			for (Field field1 : fields) {
				if (key.toLowerCase().equals(field1.getName().toLowerCase())) {
					return field1;
				}
				// 增加从注解方式获取属性名称
				if (field1.isAnnotationPresent(Column.class)) {
					Column column = field1.getAnnotation(Column.class);
					String columnName = column.name();
					if (key.toLowerCase().equals(columnName)) {

						return field1;
					}
				}
			}
			field = clazz.getDeclaredField(key);// 此方法只能获取子类中的属性，但是父类中的属性就没法取到了。
		} catch (NoSuchFieldException e) {
			Class clazz1 = clazz.getSuperclass();// 获取父类
			field = getField(clazz1, key);
		}
		return field;
	}

	/**
	 * 从具有属性扩展的bo类转换成纯map以方便解析成json数据
	 * 
	 * @param obj
	 *            具有属性扩展的BO类
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 */
	public static Map<String, Object> fromPropExpandToMap(Object obj) throws IllegalArgumentException,
			IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException {
		Map<String, Object> resultMap = new HashMap<>();
		Field[] fields = obj.getClass().getDeclaredFields();
		Field[] supFields = obj.getClass().getSuperclass().getDeclaredFields();
		Field[] allFields = ArrayUtils.addAll(fields, supFields);
		Class[] param = {};
		Object[] excParam = {};
		for (Field field : allFields) {
			if (!field.isAnnotationPresent(Column.class) && !field.isAnnotationPresent(Id.class)) {
				continue;
			}
			Method m = obj.getClass().getMethod("get" + field.getName().substring(0, 1).toUpperCase()
					+ field.getName().substring(1, field.getName().length()), param);
			if (Map.class == field.getType()) {
				resultMap.putAll((Map) m.invoke(obj, excParam));
			} else if (Date.class == field.getType()) {
				// 获取属性上的指定类型的注解
				Annotation annotation = field.getAnnotation(DateTimeFormat.class);
				if (annotation != null) {
					// 强制转化为相应的注解
					DateTimeFormat dateTimeFormat = (DateTimeFormat) annotation;
					// 获取属性上的指定类型的注解的指定方法
					if (DateUtil.PATTERN_LONG.equals(dateTimeFormat.pattern())) {
						resultMap.put(field.getName(),
								DateUtil.dateFormat((Date) m.invoke(obj, excParam), DateUtil.PATTERN_LONG));
					}
					if (DateUtil.PATTERN.equals(dateTimeFormat.pattern())) {
						resultMap.put(field.getName(),
								DateUtil.dateFormat((Date) m.invoke(obj, excParam), DateUtil.PATTERN));
					}
				} else {
					resultMap.put(field.getName(), m.invoke(obj, excParam));
				}
			} else {
				resultMap.put(field.getName(), m.invoke(obj, excParam));
			}
		}
		return resultMap;
	}

	/**
	 * 从list<map>转换为list<object>
	 */
	public static <T> List<T> fromListMapToListObject(List<Map<String, Object>> listMap, Class<T> clazz)
			throws Exception {

		if (listMap == null || listMap.size() == 0) {
			return new ArrayList<>(0);
		}
		List<T> list = new ArrayList<>(listMap.size());
		Iterator<Map<String, Object>> it = listMap.iterator();
		while (it.hasNext()) {
			Map<String, Object> map = it.next();
			T obj = ObjectTypeConvert.formMapToBO(map, clazz);
			list.add(obj);
		}
		return list;

	}

	/**
	 * 从list<object>转换为list<map>
	 */
	public static List<Map<String, Object>> fromListObjToListMap(List<Object> list) throws Exception {

		if (list == null || list.size() == 0) {
			return new ArrayList<>(0);
		}
		List<Map<String, Object>> result = new ArrayList<>(list.size());
		Iterator<Object> it = list.iterator();
		while (it.hasNext()) {
			Object obj = it.next();
			Map<String, Object> map = ObjectTypeConvert.fromPropExpandToMap(obj);
			result.add(map);
		}
		return result;
	}

	/**
	 * 验证属性数组是否跟bo属性一直( 判断属性本身，判断注解属性)
	 *
	 * @param attributes
	 *            字符串集合
	 *
	 * @param clazz
	 *            实体对象类
	 *
	 */
	public static boolean isArrayStringAttrMatchsBoAttr(Collection<String> attributes, Class clazz) {

		Field[] fields = clazz.getDeclaredFields();
		Field[] supFields = clazz.getSuperclass().getDeclaredFields();
		Field[] allFields = ArrayUtils.addAll(fields, supFields);
		List<String> fieldNams = new ArrayList<>(allFields.length);
		List<String> annotationsFieldNams = new ArrayList<>(allFields.length);
		for (Field field : allFields) {
			fieldNams.add(field.getName());
			if (field.isAnnotationPresent(Column.class)) {
				Annotation annotation = field.getAnnotation(Column.class);
				Column column = (Column) annotation;
				annotationsFieldNams.add(column.name());
			}
		}
		for (String attribute : attributes) {
			// 查询参数是否与属性一直
			if (!fieldNams.contains(attribute)) {
				// 查询参数是否与注解属性一直
				if (!annotationsFieldNams.contains(attribute)) {
					return false;
				}
			}
		}
		return true;
	}
}
